package arkham.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

import arkham.data.IdNumbered;
import arkham.data.NamedEntity;
/**
 * The Deck class adds a wrapper around an ArrayList to simulate a deck
 * of cards.
 * @author ctheng
 *
 */
public class Deck<E> implements Iterable<E>
{

	private ArrayList<E> list, tempList;

	/**
	 * Creates an empty deck.
	 */
	public Deck()
	{
		list = new ArrayList<E>();
		tempList = new ArrayList<E>();
	}

	/**
	 * Creates a deck with a set of cards
	 * @param cards The cards to add
	 */
	public Deck(Collection<E> cards)
	{
		this();
		for (E card : cards)
		{
			add(card);
		}
	}

	/**
	 * Adds a single card to the top of the deck
	 * @param card The card to add
	 */
	public void add(E card)
	{
		list.add(0, card);
	}

	/**
	 * Adds a single card to the bottom of the deck
	 * @param card The card to add
	 */
	public void addToBottom(E card)
	{
		list.add(card);
	}


	/**
	 * Discards a card from the top to the bottom of the deck
	 * @param card The card to discard
	 */
	public void discard()
	{
		E card = list.remove(0);
		list.add(card);
	}

	/**
	 * Gets the size of the deck
	 * @return The size of the deck
	 */
	public int size()
	{
		return list.size();
	}

	/**
	 * Checks if the deck is empty
	 * @return true if the deck is empty
	 */
	public boolean isEmpty()
	{
	    return list.isEmpty();
	}

	/**
	 * Shuffle the deck
	 */
	public void shuffle()
	{
		Collections.shuffle(list);
	}

	/**
	 * Deal the top card of the deck
	 * @return the card dealt
	 */
	public E deal()
	{
		if (list.isEmpty())
		{
			return null;
		}
		return list.remove(0);
	}

	/**
	 * Deals the top card of the deck into the temporary side deck
	 * @return the card dealt
	 */
	public E tempDeal()
	{
		E card = list.remove(0);
		tempList.add(card);
		return card;
	}

	/**
	 * Deals the bottom card of the deck into the temporary side deck
	 * @return the card dealt
	 */
	public E tempDealBottom()
	{
		E card = list.remove(list.size() - 1);
		tempList.add(card);
		return card;
	}

	/**
	 * Gets the temporary side deck
	 * @return the temporary side deck
	 */
	public List<E> getTempList()
	{
		return tempList;
	}

	/**
	 * Clears the temporary side deck
	 */
	public void clearTempList()
	{
		tempList.clear();
	}

	/**
	 * Discards the temp list to the bottom of the deck
	 */
	public void discardTempList()
	{
		for (int i = 0; i < tempList.size(); i++)
		{
			addToBottom(tempList.get(i));
		}
		tempList.clear();
	}

	/**
	 * Deal a random card of the deck
	 * @return the card dealt
	 */
	public E randomDeal()
	{
		Random rng = new Random();
		int pos = rng.nextInt(list.size());
		return list.remove(pos);
	}

	/**
	 * Reveal but do not discard the top card of the deck
	 * @return The card revealed
	 */
	public E revealTop()
	{
		return list.get(0);
	}

	/**
	 * Reveal but do not discard the bottom card of the deck
	 * @return The card revealed
	 */
	public E revealBottom()
	{
		return list.get(list.size() - 1);
	}

	/**
	 * Reveal but do not discard a random card of the deck
	 * @return The card revealed
	 */
	public E revealRandom()
	{
		Random rng = new Random();
		int pos = rng.nextInt(list.size());
		return list.get(pos);
	}

	/**
	 * Gets the entire deck
	 * @return The entire deck
	 */
	public List<E> getAll()
	{
		return list;
	}

	/**
	 * Deals and removes the first instance of a specific card from the deck
	 * @param card The card to deal
	 * @return The card dealt if it exists, null if it doesn't.
	 */
	public E dealSpecificCard (E card)
	{
		int pos = list.indexOf(card);
		if (pos == -1)
		{
			return null;
		}
		else
		{
			return list.remove(pos);
		}
	}

	public Iterator<E> iterator()
	{
		return list.iterator();
	}

	/**
	 * Deals and removes the first instance of a specific card from the deck
	 * @param name the name of the card
	 * @return The card dealt if it exists, null if it doesn't
	 */
	public static <E extends NamedEntity >E dealSpecificCard(Deck<E> deck, String name)
	{
		for (int i = 0; i < deck.list.size(); i++)
		{
			if (deck.list.get(i).getName().equals(name))
			{
				return deck.list.remove(i);
			}
		}
		return null;
	}

	/**
     * Deals but does not remove the first instance of a specific card from the deck
     * @param name the name of the card
     * @return The card dealt if it exists, null if it doesn't
     */
    public static <E extends NamedEntity >E viewSpecificCard(Deck<E> deck, String name)
    {
        for (int i = 0; i < deck.list.size(); i++)
        {
            if (deck.list.get(i).getName().equals(name))
            {
                return deck.list.get(i);
            }
        }
        return null;
    }

	/**
	 * Deals and removes the first instance of a specific card from the deck
	 * @param id the id of the card
	 * @return The card dealt if it exists, null if it doesn't
	 */
	public static <E extends IdNumbered >E dealSpecificCard(Deck<E> deck, int id)
	{
		for (int i = 0; i < deck.list.size(); i++)
		{
			if (deck.list.get(i).getId() == id)
			{
				return deck.list.remove(i);
			}
		}
		return null;
	}
}
